import lightgbm
import pandas as pd

# LightGBM模型经过网格搜索最终选取的最佳参数有三组：(参数顺序与下面代码一致，详情见相关代码后注释)
# ('gbdt', 'mape', 'mape', 40, 20, 180, 1, 0.7, 0.8, 5, -1, 2)
# ('gbdt', 'mape', 'mape', 40, 20, 180, 1, 0.7, 0.7, 5, -1, 2)
# ('gbdt', 'mape', 'mape', 40, 20, 200, 1, 0.7, 0.7, 5, -1, 2)
# 网格搜索中对于每组参数，将训练集数据以7:3的比例划分为新训练集与验证集，通过五折交叉验证以及wmape评价指标来排名
# 网格搜索分两次进行，第二次搜索是基于第一次搜索进行，即先搜索一部分参数的最佳值，再一次为基础继续搜索其他参数
# 分两次搜索主要原因是受限于计算机算力


# 导入数据集
train_data = pd.read_csv('train_wide_data.csv', converters={'sale_date': pd.to_datetime})
test_data = pd.read_csv('test_wide_data.csv', converters={'sale_date': pd.to_datetime})

# 参数
params = {
    'boosting_type': 'gbdt',  # 设置提升类型
    'objective': 'mape',  # 目标函数
    'metric': 'mape',  # 评估函数
    'num_leaves': 40,  # 叶子节点数
    'min_data_in_leaf': 20,  # 每个叶节点的最少样本数量
    'n_estimators': 180,  # boosted trees 的数量
    'learning_rate': 1,  # 学习速率
    'feature_fraction': 0.7,  # 建树的特征选择比例
    'bagging_fraction': 0.8,  # 建树的样本采样比例
    'bagging_freq': 5,  # k 意味着每 k 次迭代执行bagging
    'verbose': 1,  # <0 显示致命的, =0 显示错误 (警告), >0 显示信息
    'num_threads': 2,  # 给出了lightgbm 的线程数
}

# 模型训练
lgb = lightgbm.LGBMModel(**params)
lgb.fit(train_data.drop('sales_qty', axis=1), train_data['sales_qty'])

# 预测数据集
y_pred = lgb.predict(test_data, num_iteration=lgb.best_iteration_)
predict_result = pd.read_csv('test.csv',
                             usecols=['sale_date', 'goodsid'],
                             dtype={'sale_date': str, 'goodsid': int})
predict_result['sales_qty'] = y_pred
predict_result['sales_qty'] = predict_result['sales_qty'].apply(lambda x: (0 if x < 0 else x))
predict_result.to_csv('predict_result.csv', index=False)
